package com.cckj.projectapi.framework.aspect;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
  * @Description //TODO  获取接口运行时长
  * @Author wjq
  * @Param
  * @return:
  * @time: 2020/11/16 18:13
  */
@Slf4j
@Aspect
@Component
@ConditionalOnWebApplication
public class ApiRunTimeAspect {
    protected DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss", Locale.CHINA);

    @Pointcut("execution(* com.domain.projectapi.demo.*.*Controller.*(..))")
    public void runtime() {
    }

    @Around("runtime()")
    public Object logHandle(ProceedingJoinPoint pjp) throws Throwable {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        assert attributes != null;
        HttpServletRequest request = attributes.getRequest();
        String requestURI = request.getRequestURI();
        String requestMethod = request.getMethod();
        Map<String, String> basicInfo = getBasicInfo(pjp);
        Map<String, String[]> parameterMap = request.getParameterMap();
        StringBuilder params = new StringBuilder();
        for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
            params.append(entry.getKey()).append(":").append(Arrays.toString(entry.getValue())).append("\t");
        }
        long startTime = System.currentTimeMillis();
        //1.定义返回值
        Object rtValue;
        try {
            // 2.获取方法执行所需的参数
            Object[] args = pjp.getArgs();
            // 3.执行切入点方法
            rtValue = pjp.proceed(args);
            // 后置通知
            long endTime = System.currentTimeMillis();
            log.info("class:{},method:{},method-args:{},request url:{},params:{},request method:{},startTime:{},endTime:{}," +
                            "spend time:{} ms",
                    basicInfo.get("class"), basicInfo.get("method"), Arrays.toString(args), requestURI, params.toString(), requestMethod,
                    formatter.format(dateConvert(startTime)), formatter.format(dateConvert(endTime)),
                    (endTime - startTime) * 1.0f);
        } catch (Throwable t) {
            // 异常通知
            if (t instanceof Error) {
                log.error("class:{},method:{},request URL:{},request params:{},request method:{},error info:{},timeStamp:{}", basicInfo.get("class"), basicInfo.get(
                        "method"), requestURI, params.toString(), requestMethod,
                        t.getMessage(), formatter.format(LocalDateTime.now()));
            } else {
                log.warn("class:{},method:{},request URL:{},request params:{},request method:{},exception info:{},timeStamp:{}", basicInfo.get("class"), basicInfo.get(
                        "method"), requestURI, params.toString(), requestMethod,
                        t.getMessage(), formatter.format(LocalDateTime.now()));
            }
            throw  t;
        }
        return rtValue;
    }

    protected Map<String, String> getBasicInfo(JoinPoint point) {
        Map<String, String> res = new HashMap<>(2);
        //方法名
        String methodName = point.getSignature().getName();
        //类名
        String className = point.getTarget().getClass().getName();
        res.put("method", methodName);
        res.put("class", className);
        return res;
    }

    protected LocalDateTime dateConvert(Date date) {
        return date.toInstant().atOffset(ZoneOffset.of("+8")).toLocalDateTime();
    }

    protected LocalDateTime dateConvert(long millis) {
        Date date = new Date(millis);
        return dateConvert(date);
    }

}
